<?php

namespace Trawind\Basics\Traits;

use Trawind\Basics\Enums\BusinessCode;
use Trawind\Basics\Enums\BusinessType;
use Trawind\Basics\Enums\ExceptionMessage;
use Trawind\Basics\Enums\SystemConstant;
use Trawind\Helpers\HttpClientHelper;
use Trawind\Helpers\LogHelper;
use Illuminate\Support\Str;
use Trawind\Helpers\SystemHelper;

trait RepositoryTools
{
    private $extendData;

    private $remoteClassMap = BusinessType::REMOTE_MAP;

    /**
     * @param string $attribute
     * @param $id
     * @param $action
     * @return bool
     */
    public function batchWriteLog(string $controller, $logList)
    {
        if (property_exists($this->getModel(), 'businessType')) {
            $data = [];

            $actionList = LogHelper::getActionIdList($controller, array_keys($logList));

            foreach ($logList as $action => $actionIdList) {
                foreach ($actionIdList as $id) {
                    $data[] = [
                        'business_type_id' => $this->getModel()->businessType,
                        'business_id' => $id,
                        'action_id' => $actionList[$action]
                    ];
                }
            }
            if (class_exists(\TrawindCloud\Repositories\Account\Log\LogOperationRepositoryEloquent::class)) {
                return app(\TrawindCloud\Repositories\Account\Log\LogOperationRepositoryEloquent::class)->create($data);
            } else {
                return app(\Trawind\Remotes\Repositories\Account\Log\LogOperationRepositoryEloquent::class)->create($data);
            }
        }
    }

    /**
     * @param $httpClient
     * @param $data
     * @return array
     */
    public function transformMapExtendPromises($httpClient, $data)
    {
        $isRemote = request()->input('source') == 'remote';
        if ($isRemote && !in_array('business', static::needTransformRemote(), true)) {
            return [];
        }
        $promises = [];
        foreach (static::transformMapExtend() as $type => $maps) {
            if ($type == 'business') {
                $promises[$type] = $this->getListByBusinessType($httpClient, $data, $maps);
            }
        }
        return $promises;
    }

    /**
     * @param $data
     * @param $result
     * @return mixed
     */
    public function fillTransformMapExtend($data, $result)
    {
        $isRemote = request()->input('source') == 'remote';
        if ($isRemote && !in_array('business', static::needTransformRemote(), true)) {
            return $data;
        }
        foreach (static::transformMapExtend() as $type => $maps) {
            if ($type == 'business') {
                foreach ($maps as $mapKey => $map) {
                    if (is_numeric($mapKey)) {
                        $identifyKey = 'business_type_id';
                        $identifyId = 'business_id';
                    } else {
                        $identifyKey = explode(',', $mapKey)[0];
                        $identifyId = explode(',', $mapKey)[1];
                    }
                    $data = $this->setAttributes($data, explode('.', $map), $result, $identifyKey, $identifyId);
                }
            }
        }
        return $data;
    }

    /**
     * @return array
     */
    public static function transformMapExtend()
    {
        return [];
    }

    /**
     * @param $httpClient
     * @param $data
     * @param $maps
     * @return array|array[]
     */
    public function getListByBusinessType($httpClient, $data, $maps)
    {
        if (!is_array($maps)) {
            $maps = array($maps);
        }

        foreach ($maps as $mapKey => $map) {
            if (is_numeric($mapKey)) {
                $identifyKey = 'business_type_id';
                $identifyId = 'business_id';
            } else {
                $identifyKey = explode(',', $mapKey)[0];
                $identifyId = explode(',', $mapKey)[1];
            }
            $this->getAttributes($data, explode('.', $map),  $identifyKey, $identifyId);
        }

        if (!$this->extendData) {
            return [];
        }

        $result = $promises = [];
        $isRemote = request()->input('source') == 'remote';

        // select
        foreach ($this->extendData as $businessTypeID => $remoteIds) {
            if (!$businessTypeID) {
                continue;
            }
            $remoteClass = $this->remoteClassMap[$businessTypeID] ?? SystemHelper::getBusinessTypeEloquent($businessTypeID);
            if (Str::contains($remoteClass, 'Trawind\Remotes')) {
                $remote = app($remoteClass);
                $promises[$businessTypeID] = $httpClient->getAsync($remote->baseRoute,$remote->buildPromiseOptions(array_unique($remoteIds)));
            } else {
                $result[$businessTypeID] = app($remoteClass)->getListById(array_unique($remoteIds));
            }
        }

        return ['result' => $result, 'promises' => $promises];
    }

    /**
     * @param $data
     * @param $mapArr
     */
    private function getAttributes($data, $mapArr, $identifyKey, $identifyId)
    {
        if (!$mapArr) {
            $this->extendData[$data[$identifyKey]][] = $data[$identifyId];
            return ;
        }
        $str = array_shift($mapArr);
        if ($str == '*') {
            foreach ($data as $key => $val) {
                $this->getAttributes($val, $mapArr, $identifyKey, $identifyId);
            }
        } else {
            $this->getAttributes($data[$str], $mapArr, $identifyKey, $identifyId);
        }
    }

    /**
     * @param $data
     * @param $mapArr
     * @param $result
     * @return mixed
     */
    private function setAttributes($data, $mapArr, $result, $identifyKey, $identifyId)
    {
        if (!$mapArr) {
            $data[substr($identifyId,0, -3)] = ['data' => $result[$data[$identifyKey]][$data[$identifyId]] ?? []];
            return $data;
        }
        $str = array_shift($mapArr);
        if ($str == '*') {
            foreach ($data as $key => $val) {
                $data[$key] = $this->setAttributes($val, $mapArr, $result, $identifyKey, $identifyId);
            }
        } else {
            $data[$str] =$this->setAttributes($data[$str], $mapArr, $result, $identifyKey, $identifyId);
        }
        return $data;
    }

    /**
     * @param array $ids
     * @return array
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function batchEnable($ids)
    {
        // find datas
        $datas = $this->model
            ->whereIn('id', $ids)
            ->get()
            ->keyBy('id');

        // update
        $returnData = [];
        foreach ($ids as $key => $id) {
            if (!isset($datas[$id])) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::NOT_EXISTED, ['id' => $id]);
                continue;
            }

            $data = $datas[$id];

            if ($data['status_type_id'] == SystemConstant::ENABLE) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::IS_ENABLED, $data);
                continue;
            } else if ($data['status_type_id'] == SystemConstant::INVALID) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::IS_VOID, $data);
                continue;
            }

            $message = $this->batchEnableExtend($ids, $id);
            if ($message) {
                $returnData['data'][$key] = ExceptionTools::getErrData($message, $data);
                continue;
            }

            $data->update(['status_type_id' => SystemConstant::ENABLE]);
            $returnData['data'][$key]  = $data;
            $returnData['data'][$key]['message'] = ___(BusinessCode::UPDATED_OK());
        }

        return $returnData;
    }

    /**
     * @param $ids
     * @param $id
     */
    protected function batchEnableExtend($ids, $id)
    {

    }

    /**
     * @param array $ids
     * @return array
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function batchDisable($ids)
    {
        // find datas
        $datas = $this->model
            ->whereIn('id', $ids)
            ->get()
            ->keyBy('id');

        // update
        $returnData = [];
        foreach ($ids as $key => $id) {
            if (!isset($datas[$id])) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::NOT_EXISTED, ['id' => $id]);
                continue;
            }

            $data = $datas[$id];

            if ($data['status_type_id'] == SystemConstant::DISABLE) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::IS_DISABLED, $data);
                continue;
            } else if ($data['status_type_id'] == SystemConstant::INVALID) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::IS_VOID, $data);
                continue;
            }

            $message = $this->batchDisableExtend($ids, $id);
            if ($message) {
                $returnData[$key] = ExceptionTools::getErrData($message, $data);
                continue;
            }

            $data->update(['status_type_id' => SystemConstant::DISABLE]);
            $returnData['data'][$key]  = $data;
            $returnData['data'][$key]['message'] = ___(BusinessCode::UPDATED_OK());
        }
        return $returnData;
    }

    /**
     * @param $ids
     * @param $id
     */
    protected function batchDisableExtend($ids, $id)
    {

    }

    /**
     * @param array $ids
     * @return array
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function batchInvalid($ids)
    {
        // find $datas
        $datas = $this->model
            ->whereIn('id', $ids)
            ->get()
            ->keyBy('id');

        // update
        $returnData = [];
        foreach ($ids as $key => $id) {
            if (!isset($datas[$id])) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::NOT_EXISTED, ['id' => $id]);
                continue;
            }

            $data = $datas[$id];

            if ($data['status_type_id'] == SystemConstant::INVALID) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::IS_VOID, $data);
                continue;
            }

            $message = $this->batchInvalidExtend($ids, $id);
            if ($message) {
                $returnData['data'][$key] = ExceptionTools::getErrData($message, $data);
                continue;
            }

            $data->update(['status_type_id' => SystemConstant::INVALID]);
            $returnData['data'][$key]  = $data;
            $returnData['data'][$key]['message'] = ___(BusinessCode::UPDATED_OK());
        }
        return $returnData;
    }

    /**
     * @param $ids
     * @param $id
     */
    protected function batchInvalidExtend($ids, $id)
    {

    }

    /**
     * @param array $ids
     * @return array
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function batchDeletes($ids)
    {
        // find $datas
        $datas = $this->model
            ->whereIn('id', $ids)
            ->get()
            ->keyBy('id');

        // delete
        $returnData = [];
        foreach ($ids as $key => $id) {
            if (!isset($datas[$id])) {
                $returnData['data'][$key] = ExceptionTools::getErrData(ExceptionMessage::NOT_EXISTED, ['id' => $id]);
                continue;
            }
            $data = $datas[$id];

            $message = $this->batchDeleteExtend($ids, $id, $data);
            if ($message) {
                $returnData['data'][$key] = ExceptionTools::getErrData($message, $data);
                continue;
            }

            $data->delete();
            $returnData['data'][$key] = $data;
            $returnData['data'][$key]['message'] = ___(BusinessCode::DELETED_OK());
        }
        return $returnData;
    }

    /**
     * @param $ids
     * @param $id
     */
    protected function batchDeleteExtend($ids, $id, $data)
    {

    }

    /**
     * @return string[]
     */
    public function mergeSearch($field, $allowTypeData)
    {
        $request = request();
        $allowTypeStr = $field . ':' . implode(',', $allowTypeData);
        $search = config('repository.criteria.params.search');
        $getSearch = $request->get($search);
        if ($getSearch) {
            $isHave = false;
            $tempData = explode(';', $getSearch);
            foreach ($tempData as $key => $val) {
                $temp = explode(':', $val);
                if ($temp[0] == $field) {
                    $isHave = true;
                    $intersectData = array_intersect($allowTypeData, explode(',', $temp[1]));
                    if ($intersectData) {
                        $tempData[$key] = $temp[0] . ':' . implode(',', $intersectData);
                    } else {
                        $tempData[] = $field . ':0';
                    }
                }
            }
            $isHave ? : array_push($tempData, $allowTypeStr);
            $searchValue = implode(';', $tempData);
        } else {
            $searchValue = $allowTypeStr;
        }
        $searchFields = config('repository.criteria.params.searchFields');
        $searchFieldsValue = $request->get($searchFields) ? $request->get($searchFields) . ';' . $field . ':in' : $field . ':in';
        $request->merge([
            $search => $searchValue,
            $searchFields => $searchFieldsValue
        ]);
    }

}
